信じるか信じないかはさておき、暗号化されたデータを使ってコンピューテーションすることも可能です。全ての変数が暗号化されているような環境でもプログラムは実行できるんです。
このチュートリアルでは、暗号化されたデータを使ってのコンピューテーションの基本的なツールについて学習します。特にSecure Multi-Party Computationと呼ばれる人気のアプローチを紹介します。このレッスンでは、暗号化されたデータで実行できる暗号化された計算機をつくります。
Authors:
References:
SMPCは一見するととても変わった暗号手法です。公開鍵、秘密鍵のペアでの暗号化を行うかわりに、データは複数のshares
(数字)に分割されます。そして、それぞれが秘密鍵のように扱われます。一般的にはこれらのshares
は2人以上の所有者に分散されます。暗号化されたデータを複合化するためには全ての所有者が複合化を認める必要があります。別の言い方をすると、全ての所有者がそれぞれ秘密鍵を持っているということです。
では、変数x
を暗号化するにはどうすれば良いでしょう。以下の方法で可能です。
暗号化は浮動小数点数や実数を使いません。その代わりにinteger quotient ringと呼ばれる数学空間を使います。基本的には
0
からQ-1
までの整数です。Q
は実験で扱う数字を扱うに足るだけの十分に大きな素数です。実際にはあ与えられた数x
に対してx % Q
を行う事で環を実現します。(そのため、x' > Q
の状態は避ける必要があります)
In [ ]:
Q = 1234567891011
In [ ]:
x = 25
In [ ]:
import random
def encrypt(x):
share_a = random.randint(-Q,Q)
share_b = random.randint(-Q,Q)
share_c = (x - share_a - share_b) % Q
return (share_a, share_b, share_c)
In [ ]:
encrypt(x)
In [ ]:
def decrypt(*shares):
return sum(shares) % Q
In [ ]:
a,b,c = encrypt(25)
In [ ]:
decrypt(a, b, c)
重要な事は、3つの内、2つのshares
だけだと複合化は上手くいかないという事です。
In [ ]:
decrypt(a, b)
という事は、複合化には全ての所有者の参加が必要だということです。これは、shares
が秘密鍵のように機能することを意味します。
In [ ]:
x = encrypt(25)
y = encrypt(5)
In [ ]:
def add(x, y):
z = list()
# 一人目のワーカーが自分のshareを足し合わせます
z.append((x[0] + y[0]) % Q)
# 二人目のワーカーが自分のshareを足し合わせます
z.append((x[1] + y[1]) % Q)
# 三人目のワーカーが自分のshareを足し合わせます
z.append((x[2] + y[2]) % Q)
return z
In [ ]:
decrypt(*add(x,y))
In [ ]:
import torch
import syft as sy
hook = sy.TorchHook(torch)
bob = sy.VirtualWorker(hook, id="bob")
alice = sy.VirtualWorker(hook, id="alice")
bill = sy.VirtualWorker(hook, id="bill")
In [ ]:
x = torch.tensor([25])
In [ ]:
x
In [ ]:
encrypted_x = x.share(bob, alice, bill)
In [ ]:
encrypted_x.get()
In [ ]:
bob._objects
In [ ]:
x = torch.tensor([25]).share(bob, alice, bill)
In [ ]:
# Bobのshare
bobs_share = list(bob._objects.values())[0]
bobs_share
In [ ]:
# Aliceのshare
alices_share = list(alice._objects.values())[0]
alices_share
In [ ]:
# Billのshare
bills_share = list(bill._objects.values())[0]
bills_share
その気になれば、理論通りに、変数を複合化することができます。
In [ ]:
Q = x.child.field
(bobs_share + alices_share + bills_share) % Q
見ての通りですが、ここで.share()
と呼んでいるものは、一つの変数を三分割したもので、それぞれ別のワーカーへ送られています。
In [ ]:
x = torch.tensor([25]).share(bob,alice)
y = torch.tensor([5]).share(bob,alice)
In [ ]:
z = x + y
z.get()
In [ ]:
z = x - y
z.get()
In [ ]:
crypto_provider = sy.VirtualWorker(hook, id="crypto_provider")
In [ ]:
x = torch.tensor([25]).share(bob,alice, crypto_provider=crypto_provider)
y = torch.tensor([5]).share(bob,alice, crypto_provider=crypto_provider)
In [ ]:
# 乗算
z = x * y
z.get()
行列を使った乗算にも使えます。
In [ ]:
x = torch.tensor([[1, 2],[3,4]]).share(bob,alice, crypto_provider=crypto_provider)
y = torch.tensor([[2, 0],[0,2]]).share(bob,alice, crypto_provider=crypto_provider)
In [ ]:
# 行列演算
z = x.mm(y)
z.get()
暗号化された値どうしの比較も可能です。SecureNNというプロトコルは必要になりますが、それだけです。結果ももちろん暗号化されています。SecureNNの詳細はこちらでご確認ください。
In [ ]:
x = torch.tensor([25]).share(bob,alice, crypto_provider=crypto_provider)
y = torch.tensor([5]).share(bob,alice, crypto_provider=crypto_provider)
In [ ]:
z = x > y
z.get()
In [ ]:
z = x <= y
z.get()
In [ ]:
z = x == y
z.get()
In [ ]:
z = x == y + 20
z.get()
最大値を取得する演算も実行できます。
In [ ]:
x = torch.tensor([2, 3, 4, 1]).share(bob,alice, crypto_provider=crypto_provider)
x.max().get()
In [ ]:
x = torch.tensor([[2, 3], [4, 1]]).share(bob,alice, crypto_provider=crypto_provider)
max_values, max_ids = x.max(dim=0)
max_values.get()
本チュートリアルを完了しました。おめでとうございます!もし、このチュートリアルを気に入って、プライバシーに配慮した非中央集権的なAI技術や付随する(データやモデルの)サプライチェーンにご興味があって、プロジェクトに参加したいと思われるなら、以下の方法で可能です。
一番簡単に貢献できる方法はこのGitHubのレポジトリにスターを付けていただくことです。スターが増えると露出が増え、より多くのデベロッパーにこのクールな技術の事を知って貰えます。
最新の開発状況のトラッキングする一番良い方法はSlackに入ることです。 下記フォームから入る事ができます。 http://slack.openmined.org
コミュニティに貢献する一番良い方法はソースコードのコントリビューターになることです。PySyftのGitHubへアクセスしてIssueのページを開き、"Projects"で検索してみてください。参加し得るプロジェクトの状況を把握することができます。また、"good first issue"とマークされているIssueを探す事でミニプロジェクトを探すこともできます。
もし、ソースコードで貢献できるほどの時間は取れないけど、是非何かサポートしたいという場合は、寄付をしていただくことも可能です。寄附金の全ては、ハッカソンやミートアップの開催といった、コミュニティ運営経費として利用されます。
In [ ]: